package com.dave.weixin.common.nio.channel;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Created by Dave on 2018/7/1
 * Describes FileChannel
 *
 * @author Dave
 */
public class FileChannelTest {

    ///////////////////////////////////////////////////////////////////////////
    // channel：通道，像流一样，数据可以从Channel读到Buffer中，也可以从Buffer 写到Channel中 
    ///////////////////////////////////////////////////////////////////////////


    public static void main(String[] args) throws IOException {
        //可读可写 可以指定位置
        RandomAccessFile raf = new RandomAccessFile("/Users/mac/Downloads/file.txt", "rw");

        FileChannel inChannel = raf.getChannel();
        //创建一个初始化大小为48字节的buffer
        ByteBuffer buffer = ByteBuffer.allocate(48);
        int bytesRead = inChannel.read(buffer);

        while (bytesRead != -1) {
            System.out.println("read" + bytesRead);
            //将buffer从写模式切换到读取模式
            buffer.flip();
            //从buffer中读取
            while (buffer.hasRemaining()) {
                //每次读取一个字节
                System.out.println((char) buffer.get());
            }
            buffer.clear();
            //继续读取
            bytesRead = inChannel.read(buffer);
        }

        raf.close();

    }


    /**
     * fileChannel读写操作
     *
     * @throws IOException
     */
    private static void fileChannelExample() throws IOException {
        //fileChannel无法直接打开
        // 需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例
        RandomAccessFile raf = new RandomAccessFile("/Users/mac/Downloads/file.txt", "rw");
        FileChannel fileChannel = raf.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(48);
        //读取数据 返回-1就到了文尾
        int read = fileChannel.read(buffer);

        //写数据
        String date = "write string to file...";
        buffer.clear();
        buffer.put(date.getBytes());
        //切换为读模式
        buffer.flip();
        //还有东西就继续读取
        while (buffer.hasRemaining()) {
            fileChannel.write(buffer);
        }
        //关闭通道
        fileChannel.close();

    }

    /**
     * 设置fileChannel的读写位置
     *
     * @throws IOException
     */
    private void fileChannelPosition() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("/Users/mac/Downloads/file.txt", "rw");
        FileChannel fileChannel = raf.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(48);

        //在FileChannel的某个特定位置进行数据的读/写操作
        long readPos = fileChannel.position();
        fileChannel.position(readPos + 1024);
        //如果将位置设置在文件结束符之后，然后试图从文件通道中读取数据，读方法将返回-1
        fileChannel.read(buffer);

        //如果将位置设置在文件结束符之后，然后向通道中写数据，文件将撑大到当前位置并写入数据。
        //这可能导致“文件空洞”，磁盘上物理文件中写入的数据间有空隙。
        long writePos = fileChannel.position();
        fileChannel.position(writePos + 1024);
        buffer.flip();
        while (buffer.hasRemaining()) {
            fileChannel.write(buffer);
        }

    }

    /**
     * 关联文件大小
     *
     * @throws IOException
     */
    private void fileChannelSize() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("/Users/mac/Downloads/file.txt", "rw");
        FileChannel fileChannel = raf.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(48);

        //获得通道所关联的文件实例大小
        long size = fileChannel.size();
        System.out.println("文件大小：" + size);

        //文件截取 将指定长度后面删除
        fileChannel.truncate(1024);

        //将通道的数据及时写入磁盘
        //操作系统会将文件缓存在内存 如果需要及时写入需要调动force()
        //boolean类型的参数，指明是否同时将文件元数据（权限信息等）写到磁盘上。
        fileChannel.force(true);
    }


}
